

#include "solverFactory.hpp"

#include "DILUPrecond.hpp"
#include "DiagPrecond.hpp"
#include "PBiCGStab.hpp"
#include "csrMatrix.hpp"
#include "relaxedJacobiSmoother.hpp"

#define RETURNBYTYPE(expr) \
  return new expr(*dynamic_cast<const CSRMatrix<scalar> *>(&A));

namespace UNAP {

HASHMAP<string, precondCreatorPtr> PrecondFactory::precondMap_;
Preconditioner<scalar> *diluPrecondCreator(const Matrix<scalar> &A) {
  RETURNBYTYPE(DILUPrecond<scalar>);
}
Preconditioner<scalar> *diagPrecondCreator(const Matrix<scalar> &A) {
  RETURNBYTYPE(DiagPrecond<scalar>);
}

class AddPrecondToTable {
  const string name_;

 public:
  AddPrecondToTable(const string &name, precondCreatorPtr ptr) : name_(name) {
    if (PrecondFactory::precondMap_.find(name) ==
        PrecondFactory::precondMap_.end())
      PrecondFactory::precondMap_[name] = ptr;
  }
  ~AddPrecondToTable() {
    if (PrecondFactory::precondMap_.find(name_) !=
        PrecondFactory::precondMap_.end())
      PrecondFactory::precondMap_.erase(name_);
  }
};
AddPrecondToTable diluPrecond("DILU", diluPrecondCreator);
AddPrecondToTable diagPrecond("Jacobi", diagPrecondCreator);

HASHMAP<string, solverCreatorPtr> SolverFactory::solverMap_;
Solver<scalar> *bicgstabSolverCreator(Preconditioner<scalar> &precond) {
  return new PBiCGStab<scalar>(precond);
}

class AddSolverToTable {
  const string name_;

 public:
  AddSolverToTable(const string &name, solverCreatorPtr ptr) : name_(name) {
    if (SolverFactory::solverMap_.find(name) == SolverFactory::solverMap_.end())
      SolverFactory::solverMap_[name] = ptr;
  }
  ~AddSolverToTable() {
    if (SolverFactory::solverMap_.find(name_) !=
        SolverFactory::solverMap_.end())
      SolverFactory::solverMap_.erase(name_);
  }
};
AddSolverToTable bicgSolver("BiCGStab", bicgstabSolverCreator);

HASHMAP<string, smootherCreatorPtr> SmootherFactory::smootherMap_;
Smoother<scalar> *rJacobiSmootherCreator(Communicator *comm) {
  return new RelaxedJacobiSmoother<scalar>(comm);
}

class AddSmootherToTable {
  const string name_;

 public:
  AddSmootherToTable(const string &name, smootherCreatorPtr ptr) : name_(name) {
    if (SmootherFactory::smootherMap_.find(name) ==
        SmootherFactory::smootherMap_.end())
      SmootherFactory::smootherMap_[name] = ptr;
  }
  ~AddSmootherToTable() {
    if (SmootherFactory::smootherMap_.find(name_) !=
        SmootherFactory::smootherMap_.end())
      SmootherFactory::smootherMap_.erase(name_);
  }
};
AddSmootherToTable rJacobiSmoother("Jacobi", rJacobiSmootherCreator);

}  // namespace UNAP
